Link to this headingRIPEMD-160

Link to this headingExample

import hashlib, binascii ripemd160_hash = hashlib.new('ripemd160', b'hello') print("ripemd-160('hello') =", binascii.hexlify(ripemd160_hash.digest())) #ripemd-160('hello') = b'108f07b8382412612c048d07d13f814118445acd'

Link to this headingImplementation

from cryptopals_lib import * class ripemd(): """docstring for ripemd""" def __init__(self): self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0] self.buffer_indexes = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8, 3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12, 1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2, 4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13, 5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12, 6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2, 15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13, 8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14, 12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11] self.rotate_index = [11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8, 7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12, 11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5, 11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12, 9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6, 8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6, 9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11, 9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5, 15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8, 8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11] self.round_variables = [0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E, 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000] self.block_size = 64 self.message_end = 0x80 def _set_message(self, message): # Convert to bytes if not already byte_message = bytearray(message) # Get Length shifted by 8 and limit to 64bit int input_length_data = len(byte_message) # Append 0x80 to the end of the message as a end of message byte byte_message.append(self.message_end) # Pad the data until the number of bits are equally divisible by the rate. # Not including the 8 bytes for the length added at the end while (len(byte_message) + 8) % self.block_size != 0: byte_message.append(0x00) # Append the length data to the message. But first convert to bits byte_message += int_to_bytes_length(input_length_data << 3, 8, False) # Convert to the number of bits return byte_message def hash(self, message): # Setup message with padding and length data byte_message = self._set_message(message) # Operate on each of the block_size 64 bit chunks for chunk in to_blocks(byte_message, self.block_size): #print(f"Chunk: {chunk}, {len(chunk)}") self._hash_message_chunk(chunk) # Convert Integers to Byte string return intarray_to_bytes(self.state, 4) def hash_digest(self, message): return self.hash(message).hex() def _round_opperation(self, round_idx, temp_buffers, block_buffers): round_number = round_idx // 16 # Round operation depends on the round number if round_number == 0 or round_number == 9: # Do round operation #Round 1 and 10: x ^ y ^ z temp = temp_buffers[0] + (temp_buffers[1] ^ temp_buffers[2] ^ temp_buffers[3]) + self.round_variables[round_number] # Do round operations #Do round Operations #Round 2 and 9: (x & y) | (((~x) % 0x100000000) & z) temp = temp_buffers[0] + ((temp_buffers[1] & temp_buffers[2]) | asint32(~temp_buffers[1]) & temp_buffers[3]) + self.round_variables[round_number] # Do round operations #Do round Operations #Round 3 and 8: (x | ((~y) % 0x100000000)) ^ z temp = temp_buffers[0] + ((temp_buffers[1] | asint32(~temp_buffers[2])) ^ temp_buffers[3]) + self.round_variables[round_number] # Do round operations #Do round Operations #Round 4 and 7: (x & z) | (((~z) % 0x100000000) & y) temp = temp_buffers[0] + ((temp_buffers[1] & temp_buffers[3]) | asint32(~temp_buffers[3]) & temp_buffers[2]) + self.round_variables[round_number] # Do round operations #Do round Operations #Round 5 and 6: x ^ (y | ((~z) % 0x100000000)) temp = temp_buffers[0] + (temp_buffers[1] ^ (temp_buffers[2] | asint32(~temp_buffers[3]))) + self.round_variables[round_number] raise Exception("Invalid round") raise Exception("Invalid Round") # Set the two buffers #Set the Two buffers #print(round_number, temp, block_buffers[self.buffer_indexes[round_idx]] ) temp_buffers[0] = asint32(shift_rotate_left(asint32(temp + block_buffers[self.buffer_indexes[round_idx]]), self.rotate_index[round_idx]) + temp_buffers[4]) temp_buffers[2] = shift_rotate_left(temp_buffers[2], 10) #Rotate tempbuffers a,b,c,d,e -> e,a,b,c,d return [temp_buffers[4], temp_buffers[0], temp_buffers[1], temp_buffers[2], temp_buffers[3]] # Convert blocks to 32-bit ints #Convert Blocks to 32bit ints block_buffers = bytes_to_intarray(block, 4) # Clone internal buffers #Clone Internal Buffers temp_buffers = self.state[:] # Do initial 5 rounds #Do Initial 5 rounds for idx in range(16*5): #print(temp_buffers) temp_buffers = self._round_opperation(idx, temp_buffers, block_buffers) # Save the output and reset the temp buffers half_round_outputs = temp_buffers temp_buffers = self.state[:] # Do final 5 rounds for idx in range(16*5, 16*10): temp_buffers = self._round_opperation(idx, temp_buffers, block_buffers) #print(f"state: {self.state}") #print(f"half_round_outputs: {half_round_outputs}") #print(f"temp_buffers: {temp_buffers }") #Set new internal buffers # Set new internal buffers self.state[1] = asint32(self.state[2] + half_round_outputs[3] + temp_buffers[4]) self.state[2] = asint32(self.state[3] + half_round_outputs[4] + temp_buffers[0]) self.state[3] = asint32(self.state[4] + half_round_outputs[0] + temp_buffers[1]) self.state[4] = asint32(self.state[0] + half_round_outputs[1] + temp_buffers[2]) self.state[0] = temp if __name__ == '__main__': new = ripemd() print(new.hash_digest(b'hello this is a test')) #f51960af7dd4813a587ab26388ddab3b28d1f7b4